#include "StdAfx.h"
#include "Pattern.h"
#include "Triplet.h"

namespace SpikingNeuronLib
{
	// Interface Members -------------------------------------------------------------------------------------------

	bool Pattern::Equals(Object^ other, System::Collections::IEqualityComparer^ comparer)
	{
		Pattern^ otherPattern = (Pattern^) other;
		bool isEqual = this->Count == otherPattern->Count;

		if (isEqual)
		{
			List<FiringEvent>^ otherPatternEvents = otherPattern->EventList;
			for (int i = 0; isEqual && (i < this->Count); i++)
			{
				FiringEvent thisEvent = _firings[i];
				FiringEvent otherEvent = otherPatternEvents[i];
				isEqual &= (comparer->Equals(thisEvent, otherEvent));
			}
		}

		return isEqual;
	}

	int Pattern::GetHashCode(System::Collections::IEqualityComparer^ comparer)
	{
		int result = 0;

		for each (FiringEvent^ firingEvent in _firings)
		{
            result = (result * 31) ^ comparer->GetHashCode(firingEvent);
		}

		return result;
	}

	// Private Members -------------------------------------------------------------------------------------------

	HashSet<int>^ Pattern::GetPatternNeurons()
	{
		// create a set of the neurons in this Pattern
		HashSet<int>^ neurons = gcnew HashSet<int>();

		for (int i = 0; i < _firings->Count; i++)
		{
			neurons->Add(_firings[i].NeuronIndex);
		}

		return neurons;
	}

	Triplet^ Pattern::GetInitialTriplet()
	{
		Triplet^ initialTriplet = nullptr;
		if (_firings->Count >= 3)
		{
			initialTriplet = gcnew Triplet(_firings[0], _firings[1], _firings[2]);
		}

		return initialTriplet;
	}

	List<FiringEvent>^ Pattern::ConvertFromStringImpl(String^ firingsString)
	{
		List<FiringEvent>^ firings = gcnew List<FiringEvent>();
		array<String^>^ parts = firingsString->Split(gcnew array<Char> { '(', ')' });
		using namespace System::Text::RegularExpressions;
		Regex^ re = gcnew Regex(L"^\\s*([0-9]+)\\s*,\\s*([0-9]+)\\s*,\\s*([FalseTrue]+)\\s*$");
		for (int i = 0; i < parts->Length; i++)
		{
			String^ item = parts[i];
			System::Text::RegularExpressions::Match^ m = re->Match(item);
			if (m->Success)
			{
				int time;
				int nindex;
				if (Int32::TryParse(m->Groups[1]->Value, time) && Int32::TryParse(m->Groups[2]->Value, nindex))
				{
					FiringEvent fe = FiringEvent(time, nindex, EventLabel::Unspecified);
					firings->Add(fe);
				}
			}
		}

		return firings;
	}

	String^ Pattern::ConvertToStringImpl(List<FiringEvent>^ firings)
	{
		using namespace System::Text;
		StringBuilder^ sb = gcnew StringBuilder();
		for (int i = 0; i < firings->Count; i++)
		{
			sb->Append(firings[i].ToString());
			if (i < firings->Count - 1)
				sb->Append(", ");
		}

		return sb->ToString();
	}

	// Public Members -------------------------------------------------------------------------------------------

	Pattern::Pattern(String^ firingEvents)
	{
		_uniqueID = 0;
		_firings = Pattern::ConvertFromStringImpl(firingEvents);
	}

	Pattern::Pattern(System::Collections::Generic::IEnumerable<FiringEvent>^ firingEvents)
	{
		_uniqueID = 0;
		_firings = gcnew List<FiringEvent>(firingEvents);
	}

	Pattern::Pattern(System::Collections::Generic::IEnumerable<Tuple<int, int>^>^ firingEvents)
	{
		_uniqueID = 0;
		_firings = gcnew List<FiringEvent>();

		for each (Tuple<int, int>^ pair in firingEvents)
		{
			FiringEvent evt = FiringEvent(pair->Item1, pair->Item2, EventLabel::Unspecified);
			_firings->Add(evt);
		}
	}

	Pattern::Pattern(array<FiringEvent>^ firingArray)
	{
		_uniqueID = 0;
		_firings = gcnew List<FiringEvent>(firingArray);
	}

	Pattern::Pattern(List<FiringEvent>^ firingsList)
	{
		_uniqueID = 0;
		_firings = firingsList;
	}

	Pattern::Pattern(List<FiringEvent>^ firingsList, int uniqueID)
	{
		_uniqueID = uniqueID;
		_firings = firingsList;
	}

	Pattern::Pattern(Pattern^ parentPattern, int maxFiringEvents)
	{
		List<FiringEvent>^ parentFirings = parentPattern->_firings;
		int maxEvents = (maxFiringEvents <= parentFirings->Count)? maxFiringEvents : parentFirings->Count;

		_uniqueID = parentPattern->_uniqueID;
		_firings = parentFirings->GetRange(0, maxEvents);
	}

	String^ Pattern::ToString()
	{
		return Pattern::ConvertToStringImpl(_firings);
	}

	System::Collections::Generic::IEnumerable<Tuple<int, int>^>^ Pattern::GetEventTuples()
	{
		List<Tuple<int, int>^>^ eventTuples = gcnew List<Tuple<int, int>^>(_firings->Count);

		for each (FiringEvent evt in _firings)
		{
			Tuple<int, int>^ pair = Tuple::Create(evt.Time, evt.NeuronIndex);
			eventTuples->Add(pair);
		}

		return safe_cast<System::Collections::Generic::IEnumerable<Tuple<int, int>^>^>(eventTuples);
	}

	System::Collections::Generic::IEnumerable<FiringEvent>^ Pattern::GetFiringEvents()
	{
		return safe_cast<System::Collections::Generic::IEnumerable<FiringEvent>^>(_firings);
	}

	String^ Pattern::ConvertToString(Pattern^ pattern)
	{
		return Pattern::ConvertToStringImpl(pattern->EventList);
	}

	Pattern^ Pattern::ConvertFromString(String^ firingsString)
	{
		List<FiringEvent>^ firingsList = Pattern::ConvertFromStringImpl(firingsString);

		return gcnew Pattern(firingsList);
	}
}